/*==========================================================================
 *
 *  Copyright (C) 1995 Microsoft Corporation. All Rights Reserved.
 *	Source		:	DSstream.h
 *  File		:	DsApi.h
 *	Edited by	:	Liu Gang
 *	Version		:	V0023
 *	V0010		:	Oct.8.1996
 *	V0011		:	Nov.25.1996
 *	V0012		:	Apr.1.1997, add IfPlaying() function to classes
 *	V0020		:	Apr.2.1997, fixed a bug when playing static sound, 
 *								do not use Mutex with static wave objects
 *	V0021		:	May.2.1997, add function DS_IfSoundEnable()
 *	V0022		:	May.10.1997, fixed a bug in IfPlaying(), close the sound if the sound is end
 *	V0023		:	May.15.1997, fixed bugs when quit and init sound frequently
 *	V0024		:	Aug.13.1997, fixed a bug when playing
 *  Content:    DirectSound untilities Header
 *
 ***************************************************************************/
#ifndef __DSAPI_H__
#define __DSAPI_H__

///////////////////
#include <mmsystem.h>
#include <dsound.h>
///////////////////

// error id
#define	DS_ERROR_ID		300

/* WARNING -- Be careful about changing NUM_BUFFER_SEGMENTS -- it should be at
 * least 3, or there will probably not be enough time for the interrupt to do
 * its thing before the player catches up to it.  Also, PLAYBACK_TIMER_PERIOD
 * is set at 200 milliseconds because we are OVERSAMPLING by a factor of four.
 * This is so we can shift the frequency on the fly and still keep up with the
 * play cursor.  The timer routine will only fill when necessary.  Experiments
 * have shown that the timer still operates well when interrupted every 50ms
 * (and possibly even more often than that).  If you do not require real-time
 * frequency changes, you reduce the OVERSAMPLE as well as the TIMER_PERIOD.
 * However, note that since a buffer segment is only an approximation of the
 * number of bytes played per interrupt, it's a good idea to interupt a bit more
 * often than the TIMER_PERIOD, just to make sure the timer isn't falling behind
 * the player.  If your frequency is fixed, the call to initialize the timer
 * event could be changed to first calculate something like (TIMER_PERIOD * .8).
 * This should ensure that the buffer is well maintained and system performance
 * is maximized.
 */

// macros
///////////////////
// sound device params
#define	DS_WAVE_FORMAT_AUTO			0
//#define	DS_WAVE_FORMAT_1_11_8		1
//#define	DS_WAVE_FORMAT_1_11_16		2
//#define	DS_WAVE_FORMAT_1_22_8		3
//#define	DS_WAVE_FORMAT_1_22_16		4
//#define	DS_WAVE_FORMAT_1_44_8		5
//#define	DS_WAVE_FORMAT_1_44_16		6
//#define	DS_WAVE_FORMAT_2_11_8		7
//#define	DS_WAVE_FORMAT_2_11_16		8
#define	DS_WAVE_FORMAT_2_22_8		9
#define	DS_WAVE_FORMAT_2_22_16		10
//#define	DS_WAVE_FORMAT_2_44_8		11
//#define	DS_WAVE_FORMAT_2_44_16		12


// dynamic wave parameters
#define NUM_BUFFER_SEGMENTS     6
#define PLAYBACK_TIMER_PERIOD   200
#define PLAYBACK_OVERSAMPLE     4
#define PLAYBACK_TIMER_ACCURACY 10

//#define DSSTREAM_STOPF_NOREOPEN 0x0001

// errors returned by StreamBufferSetup()
#define ERR_WAVE_OPEN_FAILED		(DS_ERROR_ID+10)
#define ERR_WAVE_INVALID_FORMAT		(DS_ERROR_ID+11)
#define ERR_CREATEDSB_FAILED		(DS_ERROR_ID+12)
#define ERR_WAVE_CORRUPTED_FILE		(DS_ERROR_ID+13)
#define	ERR_CREATEDSB_LOST			(DS_ERROR_ID+14)
#define	ERR_WAVE_FORMAT_INVALID		(DS_ERROR_ID+15)
// The values for PAN may change in range...

//#define PAN_TB_MIN              0
//#define PAN_TB_MAX              2000
//#define PAN_TB_CENTER           1000
//#define PAN_MULTIPLIER          1
#define PAN_MIN                 0
#define PAN_MAX                 800
#define PAN_CENTER              400
//#define PAN_SHIFT               (-400)
//#define PAN_PAGESIZE            10
//#define PAN_DIV                 10
//#define PAN_MULT                10

//#define PAN_DSB_MIN           (-400)
//#define PAN_DSB_MAX           400
//#define PAN_DSB_CENTER        0

//#define VOL_TB_MIN              0
//#define VOL_TB_MAX              1000
//#define VOL_MULTIPLIER          1
//#define VOL_SHIFT               (-400)
#define VOL_MIN                 -5000
#define VOL_MAX                 0
//#define VOL_PAGESIZE            10
//#define VOL_DIV                 10
//#define VOL_MULT                10
//#define FREQ_MIN                441
//#define FREQ_MAX                4410
//#define FREQ_PAGESIZE           100
//#define FREQ_MULTIPLIER         10
//#define PROG_MIN                0
//#define PROG_MAX                10000
//#define PROG_MULTIPLIER         100

// wave playing flags
#define WM_DSSTREAM_DONE        WM_USER + 0x100 /* Make our own app messages */
#define WM_DSSTREAM_DEBUG       WM_USER + 0x101
#define WM_DSSTREAM_PROGRESS    WM_USER + 0x102
///////////////////

// global controls
///////////////////
// the number of stream and static buffers you are actually using 
// will consume 30% CPU time.
// stream sound buffers cannot be more than 8, less than 2 is better.
#define	WAVE_OBJECT_MAX		8

// static sound buffers can be any, change 64 to the number you want,
// but less than 8 is better.
#define	WAVE_OBJECT_MAXS	64
#define	WAVE_OBJECT_NONE	-1
//////////////////

// dynamic wave object
//////////////////
// if you want to play a very long wave file, you'd better use this class
/*
 * This structure keeps all the data that the TimeFunc callback uses in one
 * place.  In this implementation, that means the global data segement.  This
 * is setup so that if you wanted to put your callback in a DLL, all you'd need
 * to do is pass the address of this structure as a parameter.
 */
typedef class CDynamicWave
{
public:
    WAVEFORMATEX         *pwfx;             /* Wave Format data structure */
    HMMIO                hmmio;             /* MM I/O handle for the WAVE */
    MMCKINFO             mmck;              /* Multimedia RIFF chunk */
    MMCKINFO             mmckInRIFF;        /* Use in opening a WAVE file */
    LPDIRECTSOUNDBUFFER  lpDSBStreamBuffer; /* Points to DirectSoundBuffer */
    DWORD                dwBufferSize;      /* Size of the entire buffer */
    DWORD                dwBufferSegSize;   /* Size of one buffer segment */
    DWORD                dwNextWriteOffset; /* Offset to next buffer segment */
    DWORD                dwPlayLast;        /* Stores last play cursor */
    DWORD                dwProgress;        /* Used with above to show prog. */
    BOOL                 bDonePlaying;      /* Signals early abort to timer */
    BOOL                 bLoopFile;         /* Should we loop playback? */
    BOOL                 bFoundEnd;         /* Timer found file end */
    int                  nRemainingSegments;/* Segments 'til timer shutdown */

	int		nID;							/* ID, if not equals to WAVE_OBJECT_NONE,
											the wave file is opened */
	char	strFileName[65];				/* wave file name */
	BOOL    bPlaying, bTimerInstalled;	/* if wave file is playing, 
										timer has been installed*/
	UINT    uTimerID, uLastPercent;		/* timer ID and percent has been played */

public:
	// functions
	CDynamicWave();
	~CDynamicWave();

private:
	// load sound buffer
	// filename		:	wave file name
	// return value	:	TRUE if succeeded
	BOOL Load( LPCTSTR filename );	

	// release sound buffer
	void Close();					

public:
	// play wave file
	// dwFlags		:	TRUE if looping
	// pos			:	position to play from
	// return value	:	TRUE if played
	// if this object is playing, stop and playing again
	BOOL Play( DWORD dwFlags=DSBPLAY_LOOPING, DWORD pos=0 );

	// reset wave player, avoid to use
	void Reset();

	// stop playing
	// if has stoped, no effects
	void Stop();

	// test if the sound is playing
	// returns TRUE if playing
	BOOL IfPlaying(){ return bPlaying; }

	// to make the sound lower and lower
	// return value	:	TRUE if succeeded
	BOOL FadeOut();				

	// to make the sound louder and louder
	// return value	:	TRUE if succeeded
	BOOL FadeIn();				

} WAVEDYNAMIC, *LPWAVEDYNAMIC;
//////////////////

// static wave object
//////////////////
// if the wave file is short, use this class
// struct CStaticWave
typedef	class	CStaticWave
{
public:
	char	strFileName[65];				/* wave file name */
	int		nID;							/* ID, if not equals to WAVE_OBJECT_NONE,
											the wave file is opened */
    WAVEFORMATEX      *pwfx;				/* Wave Format data structure */
    UINT	cbSize;							/* Size of the entire buffer */
    BOOL    bLoopFile;						/* Should we loop playback? */
	BOOL    bPlaying;						/* if wave file is playing */
    LPDIRECTSOUNDBUFFER  lpDSBStreamBuffer; /* Points to DirectSoundBuffer */
	BYTE	* pbData;

public:
	CStaticWave();
	~CStaticWave();
private:
	// load sound buffer
	// filename		:	wave file name
	// return value	:	TRUE if succeeded
	BOOL Load( LPCTSTR filename );

	// release sound buffer
	void Close();

public:
	// dwFlags		:	TRUE if looping
	// pos			:	position to play from
	// return value	:	TRUE if played
	// if this object is playing, stop and playing again
	// Warning:	a bug report here: the last sound in segment buffer 
	//          cannot be played, so you need the file larger than 
	//          normal at last, with a period of slience.
	BOOL Play( DWORD dwFlags=DSBPLAY_LOOPING, DWORD pos=0 );

	// stop playing
	// if has stoped, no effects
	void Stop();

	// test if the sound is playing
	// returns TRUE if playing
	BOOL IfPlaying();
}	WAVESTATIC, *LPWAVESTATIC;

//////////////////

// Global Function declarations
//////////////////
// global functions for sound object

// initialize sound device
// hwnd			:	handle of window to receive error messages
// nType		:	wave format, 
//					default if do not want to change params of waves
// return value	:	TRUE if succeeded
BOOL DS_InitSound( HWND hwnd, int nType = DS_WAVE_FORMAT_AUTO );

// release sound device
void DS_QuitSound( void ); 

// enable or disable sound effects
// bEnable		:	TRUE for enable sound
// return value	:	old state of sound, enable or disable
BOOL DS_EnableSound( BOOL bEnable );

// test if sound is enable
BOOL DS_IfSoundEnable();

// set fade in or fade out
#define	DS_FADE_NONE	0
#define	DS_FADE_IN		1
#define	DS_FADE_OUT		2
// nFade		:	fade mode, see above
// return value	:	old fade mode
// this function will take effect, only when playing dynamic wave file
// cannot fade in or fade out static wave playing
int	DS_FadeSound( int nFadeMode );

// set volume
// real range is from 0 to -10000,
// this function is range from 0 to -5000
// under -3200 I cannot hear at all
// nVol			:	volume of the sound device
// return value	:	old volume of the sound device, -1 if error occurs
int	DS_VolumeSound( int nVol );

// set pan
// real range is from -10000 to 10000, 
// but this function is range from -2000 to 2000
// larger than 1000, right cannot hear
// smaller than -1000 left cannot hear
// nPan			:	pan value for the sound device
// return value	:	old pan value for the sound device
int	DS_PanSound( int nPan );

// get sound volume
extern int DS_GetVolume();
//////////////////
#endif /* __DSAPI_H__ */

